package rattlesnake.callback.server.handler;

import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.*;
import io.netty.util.CharsetUtil;
import rattlesnake.callback.core.NettyBootAppcation;
import rattlesnake.callback.core.factory.HandlerFactory;
import rattlesnake.callback.core.strategy.HandlerAdapterContext;
import rattlesnake.callback.server.HandlerAdapter;
import rattlesnake.callback.util.MJsonBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.util.Collections;
import java.util.Set;

import static io.netty.buffer.Unpooled.copiedBuffer;
import static io.netty.handler.codec.http.HttpHeaders.Names.*;

/**
 * @Package: com.example.netty
 * @Description: 处理逻辑
 * @author: liuxin
 * @date: 17/4/11 下午3:51
 */
public class HttpPrepareServerHandler extends SimpleChannelInboundHandler<HttpObject> {

    private static final Logger logger = LoggerFactory.getLogger(HttpPrepareServerHandler.class);

    private HttpRequest request;

    private boolean readingChunks;

    private StringBuilder responseContent = new StringBuilder();

    private static final HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); //Disk

    private HttpPostRequestDecoder decoder;

    private MJsonBuilder mJsonBuilder = new MJsonBuilder();

    /**
     * 当连接通道关闭后清理
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        if (decoder != null) {
            decoder.cleanFiles();
        }
    }

    public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        /**
         * msg的类型
         * {@link DefaultHttpRequest}
         * {@link LastHttpContent}
         */
        if (msg instanceof HttpRequest) {
            HttpRequest request = this.request = (HttpRequest) msg;
            URI uri = new URI(request.getUri());
            if (uri.getPath().equals("/")) {
                writeMenu(ctx);
                return;
            }
            String[] categoryName = request.getUri().split("/");
            String category = categoryName[1];
            //TODO 根据服务获得不同的处理器
            HandlerAdapter handlerAdapter = HandlerFactory.getHandlerByCategory(category.replaceAll("/", ""));
            //TODO 根据服务获得分发下去服务的地址
            String categoryUrl = NettyBootAppcation.getCategoryUrl().get(category);
            HandlerAdapterContext context = new HandlerAdapterContext(handlerAdapter);
            context.operate(ctx, request,msg, categoryUrl);
            logger.debug("请求地址:{},访问本地址:{},访问机构IP:{}", uri.getPath(), ctx.channel().localAddress(), ctx.channel().remoteAddress());
        }
    }


    /**
     * 当未发现路径，返回菜单页面
     *
     * @param ctx
     */
    private void writeMenu(ChannelHandlerContext ctx) {
        String strVar = mJsonBuilder.set("code", 1000, Integer.class).set("message", "请检查路径名", String.class).toJson();
        ByteBuf buf = copiedBuffer(strVar, CharsetUtil.UTF_8);
        FullHttpResponse response = new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
        response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
        response.headers().set(CONTENT_LENGTH, buf.readableBytes());
        ctx.channel().writeAndFlush(response);

    }

    /**
     * exceptionCaught() 事件处理方法是当出现 Throwable 对象才会被调用，
     * 即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时。
     * 在大部分情况下，捕获的异常应该被记录下来并且把关联的 channel 给关闭掉。
     * 然而这个方法的处理方式会在遇到不同异常的情况下有不同的实现，
     * 比如你可能想在关闭连接之前发送一个错误码的响应消息。
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.debug("当前TheadName:{},ChannelName:{},处理异常关闭:{}", Thread.currentThread().getName(),ctx.channel());
        ctx.channel().close();
        throw new RuntimeException(cause);
    }

    /**
     * 读取的时候调用逻辑处理方法
     *
     * @param ctx
     * @param msg
     * @throws Exception 覆盖了 channelRead0() 事件处理方法。
     *                   每当从服务端读到客户端写入信息时，
     *                   其中如果你使用的是 Netty 5.x 版本时，
     *                   需要把 channelRead0() 重命名为messageReceived()
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        logger.debug("访问本地址:{},访问机构IP:{}", ctx.channel().localAddress(), ctx.channel().remoteAddress());
        messageReceived(ctx, msg);
    }
}
